Project Name: Classic Snake 8×8 Edition
Category: Gaming, LED Matrix, Hardware Feedback
Difficulty: Intermediate
This project brings the legendary “Snake” game to life using an 8×8 Dot Matrix display and an ESP32. You will navigate a growing snake across the grid to eat food, while managing your score on an LCD screen. To make the experience more immersive, a NeoPixel strip changes colors based on game events (like eating or crashing) and a buzzer plays retro background music!
Learning Objectives (What You Will Learn)
By building this project, you will learn to:
- Master the Dot Matrix: Use the
MD_MAX72xxlibrary to control an 8×8 grid and draw individual pixels. - Handle Multi-Button Input: Configure four buttons to control 2D movement (Up, Down, Left, Right).
- Integrate Everything: Run an LCD, NeoPixels, a Matrix, and a Buzzer all on a single ESP32.
- Code Optimization: Move note frequencies into the main script to simplify your project folder.
Circuit Connections

| Components | ESP32 Dev Module |
| Matrix CLK | IO18 |
| Matrix DATA | IO23 |
| Matrix CS | IO15 |
| LCD I2C SDA | IO21 |
| LCD I2C SCL | IO22 |
| BUTTON UP | IO33 |
| BUTTON DOWN | IO32 |
| BUTTON LEFT | IO34 |
| BUTTON RIGHT | IO35 |
| BUZZER | IO12 |
| NEOPIXEL | IO5 |
Code Lab
Step 1: Install Libraries
Open the Arduino IDE Library Manager and install:
- MD_MAX72XX by majicDesigns
- LiquidCrystal_I2C by Frank de Brabander
- Adafruit NeoPixel by Adafruit
Step 2: Main Code
Copy and paste the code provided in the prompt into your main sketch file.
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_NeoPixel.h>
#define NOTE_E4 330
#define NOTE_G4 392
#define NOTE_A4 440
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_FS4 370
#define NOTE_D4 294
#define NOTE_G5 784
#define NOTE_FS5 740
#define NOTE_A5 880
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES 1
#define CLK_PIN 18
#define DATA_PIN 23
#define CS_PIN 15
MD_MAX72XX M = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
#define BUTTON_UP 33
#define BUTTON_DOWN 32
#define BUTTON_LEFT 34
#define BUTTON_RIGHT 35
#define BUZZER_PIN 12
#define LED_PIN 5
#define NUM_LEDS 6
LiquidCrystal_I2C lcd(0x27, 16, 2);
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
int snakeX[64], snakeY[64];
int length = 2;
int foodX, foodY;
int direction = 4; // 4 = right
unsigned long lastMoveTime = 0;
unsigned long moveInterval = 200;
int score = 0;
int melody[] = {
0, NOTE_D5, NOTE_CS5, NOTE_C5, NOTE_B4, 0, NOTE_G4, NOTE_D4, NOTE_G4, NOTE_B4,
NOTE_G4, NOTE_D5, NOTE_B4, NOTE_G5, NOTE_FS5, NOTE_G5, NOTE_A5, NOTE_G5,
NOTE_FS5, NOTE_G5, NOTE_D5, NOTE_B4, NOTE_G4, NOTE_FS4, NOTE_E4, NOTE_FS4,
NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_B4, NOTE_A4
};
float noteDurations[] = {
2.666, 8, 8, 8, 8, 8, 8, 4, 8, 4, 8, 4, 8, 8, 8, 8, 8, 8,
4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 8, 8, 8, 4
};
unsigned long lastMusicUpdate = 0;
unsigned int musicIndex = 0;
void playBGM() {
if (millis() - lastMusicUpdate >= 200) {
int noteDuration = 1000 / noteDurations[musicIndex];
if (melody[musicIndex] > 0) tone(BUZZER_PIN, melody[musicIndex], noteDuration);
else noTone(BUZZER_PIN);
lastMusicUpdate = millis();
musicIndex = (musicIndex + 1) % (sizeof(melody) / sizeof(melody[0]));
}
}
void setLEDColor(uint8_t r, uint8_t g, uint8_t b) {
for (int i = 0; i < NUM_LEDS; i++) strip.setPixelColor(i, strip.Color(r, g, b));
strip.show();
}
void spawnFood() {
foodX = random(0, 8);
foodY = random(0, 8);
}
void initSnake() {
snakeX[0] = 4; snakeY[0] = 4;
snakeX[1] = 3; snakeY[1] = 4;
}
void gameOver() {
tone(BUZZER_PIN, 500, 500);
setLEDColor(255, 0, 0);
lcd.clear(); lcd.print("Game Over!");
delay(2000);
lcd.clear(); lcd.print("Press to Start");
while (digitalRead(BUTTON_UP) && digitalRead(BUTTON_DOWN) &&
digitalRead(BUTTON_LEFT) && digitalRead(BUTTON_RIGHT)) playBGM();
score = 0; length = 2; direction = 4;
lcd.clear(); initSnake(); spawnFood();
setLEDColor(0, 255, 0);
}
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_DOWN, INPUT_PULLUP);
pinMode(BUTTON_LEFT, INPUT_PULLUP);
pinMode(BUTTON_RIGHT, INPUT_PULLUP);
M.begin();
lcd.init(); lcd.setBacklight(true);
strip.begin(); setLEDColor(0, 255, 0);
lcd.print("Snake Game");
while (digitalRead(BUTTON_UP) && digitalRead(BUTTON_DOWN) &&
digitalRead(BUTTON_LEFT) && digitalRead(BUTTON_RIGHT)) playBGM();
lcd.clear(); spawnFood(); initSnake();
}
void loop() {
if (digitalRead(BUTTON_UP) == LOW) direction = 1;
if (digitalRead(BUTTON_DOWN) == LOW) direction = 2;
if (digitalRead(BUTTON_LEFT) == LOW) direction = 3;
if (digitalRead(BUTTON_RIGHT) == LOW) direction = 4;
if (millis() - lastMoveTime >= moveInterval) {
for (int i = length - 1; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
if (direction == 1) snakeY[0] = (snakeY[0] - 1 + 8) % 8;
if (direction == 2) snakeY[0] = (snakeY[0] + 1) % 8;
if (direction == 3) snakeX[0] = (snakeX[0] - 1 + 8) % 8;
if (direction == 4) snakeX[0] = (snakeX[0] + 1) % 8;
if (snakeX[0] == foodX && snakeY[0] == foodY) {
length++; score++; spawnFood();
tone(BUZZER_PIN, 1000, 100);
setLEDColor(0, 0, 255);
delay(50);
setLEDColor(0, 255, 0);
}
lastMoveTime = millis();
}
for (int i = 1; i < length; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) gameOver();
}
M.clear();
for (int i = 0; i < length; i++) M.setPoint(snakeX[i], snakeY[i], true);
M.setPoint(foodX, foodY, true);
M.update();
lcd.setCursor(0, 0); lcd.print("Snake Running!");
lcd.setCursor(0, 1); lcd.print("Score: "); lcd.print(score);
}
How to Play
- Power On: The NeoPixels turn Green and music plays.
- Move: Use the 4 buttons to navigate. The snake “wraps” around the screen edges.
- Eat: Each time you eat food, the NeoPixels flash Blue, the buzzer beeps, and you grow longer!
- Game Over: Don’t hit your own tail! If you do, the screen turns Red.
Troubleshooting Guide
| Problem | Simple Solution to Try |
| Buzzer is silent | Ensure it is connected to IO12 |
| LED Matrix is update down | Use M.setRotation(n) in setup() where n is 1, 2 or 3 to rotate the display. |
| Buttons are unresponsive | Check that each button has one pin connected to the ESP32 |
Buy from:
Myduino AIoT Education Kit from myduino.com






